package com.ybear.ybcomponent.widget.linkage.effector

import android.view.MotionEvent
import com.ybear.ybcomponent.widget.linkage.effector.base.CoreEffector
import com.ybear.ybcomponent.widget.linkage.enums.ViewLinkage
import kotlin.math.abs

/**
 * @Author MiWi
 * @Date 2025年2月13日
 * @Description Linkage - 渐变效果器
 *
 * 此效果器根据父容器滚动的距离动态改变子视图的透明度（alpha）。
 * 当滑动距离在 startOffset 到 endOffset 范围内时，透明度会在 0 到 1 之间渐变。
 *
 * 为避免在手指慢慢滑动时因频繁更新造成“若隐若现”的效果，我们引入了一个阈值（alphaThreshold），
 * 只有当当前 alpha 与目标 alpha 变化超过该阈值时，才更新 alpha。
 *
 * 同时提供了 restoreEffector 方法，通过动画平滑恢复初始状态。
 */
open class AlphaEffector : CoreEffector() {
    // 记录初始的透明度值，当用户第一次触摸时记录，用于后续恢复或作为计算基准
    open var originalAlpha: Float = Float.MIN_VALUE
        protected set

    // 记录上一次滚动的 Y 值，用于计算滚动差值或判断滑动方向
    open var lastScrollY = 0F
        protected set

    // 恢复原始透明度时使用的动画时长（毫秒）
    open var restoreEffectorDuration = 250L

    // 阈值：只有当当前 alpha 与目标 alpha 的差值大于此值时才更新，避免频繁微小变化导致视觉闪烁
    open var alphaThreshold = 0.05f

    override fun onViewLinkage(): ViewLinkage = ViewLinkage.ALPHA

    override fun onEffector(startOffset: Float, endOffset: Float): Boolean {
        // 获取当前的触摸事件
        val ev = motionEvent

        // 如果 endOffset 为0，直接返回 false，避免除以0或无效计算
        if (endOffset == 0F) return false

        when (ev.action) {
            MotionEvent.ACTION_DOWN -> {
                // ACTION_DOWN 时记录初始的透明度值（仅第一次触摸时记录）
                if (originalAlpha == Float.MIN_VALUE) {
                    originalAlpha = childView.alpha
                }
            }
            MotionEvent.ACTION_MOVE -> {
                // 使用父容器当前的垂直滚动位置作为参考
                val scrollY = parent.scrollY.toFloat()

                // 计算滑动距离对应的比例 diff：
                // 如果 scrollY 小于 startOffset，则 diff 为 0；
                // 否则 diff = (scrollY - startOffset) / (endOffset - startOffset)，并限制在 0 到 1 的范围内
                val diff = if (scrollY < startOffset) {
                    0F
                } else {
                    ((scrollY - startOffset) / (endOffset - startOffset)).coerceIn(0F, 1F)
                }

                // 获取当前子视图的 alpha 值
                val currentAlpha = childView.alpha

                // 根据当前滚动位置判断目标 alpha 值 toAlpha：
                // 1. 如果 scrollY 小于等于 0 或者 scrollY 与上一次滚动值之和为负，则目标 alpha 设为 0；
                // 2. 如果 scrollY 超过 endOffset，则目标 alpha 设为 1；
                // 3. 其它情况下按 diff 的值计算目标 alpha
                val toAlpha = when {
                    scrollY <= 0F || scrollY + lastScrollY < 0 -> 0F
                    scrollY > endOffset -> 1F
                    else -> diff
                }

                // 只有当当前 alpha 与目标 alpha 的变化超过阈值时才更新，
                // 这样可以防止在慢慢滑动时，频繁更新导致的若隐若现效果
                if (abs(currentAlpha - toAlpha) > alphaThreshold) {
                    childView.alpha = toAlpha
                }

                // 更新 lastScrollY 记录
                lastScrollY = scrollY
            }
            MotionEvent.ACTION_UP,
            MotionEvent.ACTION_CANCEL -> {
                // 在触摸结束时，使用触摸事件中的 y 坐标作为参考
                val scrollY = ev.y

                // 如果触摸结束时的滑动距离小于 startOffset，则认为条件不足，
                // 恢复子视图的原始透明度，并返回 false，不继续处理
                if (abs(scrollY) < startOffset) {
                    childView.alpha = originalAlpha
                    return false
                }

                // 计算目标 alpha 值，同 ACTION_MOVE 的计算方式
                val toAlpha = ((scrollY - startOffset) / (endOffset - startOffset)).coerceIn(0F, 1F)
                // 同样，只有当变化足够明显时更新 alpha
                if (abs(childView.alpha - toAlpha) > alphaThreshold) {
                    childView.alpha = toAlpha
                }
                // 更新记录
                lastScrollY = scrollY
            }
        }
        // 返回 false 表示该事件未被完全消费，允许其他处理
        return false
    }

    override fun restoreEffector(animator: Boolean) {
        // 如果不使用动画恢复，直接将子视图 alpha 设置为初始值
        if (!animator) {
            childView.alpha = originalAlpha
            return
        }
        // 否则使用动画从当前 alpha 平滑过渡到 originalAlpha
        createNumberAnimator(
            childView.alpha,      // 起始 alpha 值
            originalAlpha,          // 目标 alpha 值
            restoreEffectorDuration // 动画时长
        ) { alpha ->
            childView.alpha = alpha
        }.start()
    }
}